`include "common.svh"

module lsu #(
    parameter STQ_SIZE = 8
) (
    input clk,
    input rst,

    output empty,

    input i_valid,
    // input ISSUE_OP i_iop,
    input MEM_REQ i_req,
    output i_ready,

    output o_valid,
    output MEM_RSP o_rsp,
    input o_ready,

    output o_rob_finish_valid,
    output ROB_PTR o_rob_finish_rob_idx,

    input i_rob_order_valid,
    input ROB_PTR i_rob_order_rob_idx,

    //DMEM REQ
    output o_dmem_valid,
    output MEM_REQ o_dmem_req,
    input o_dmem_ready,
    output o_dmem_kill_prev,

    //DMEM RSP
    input i_dmem_valid,
    input MEM_RSP i_dmem_rsp,
    output i_dmem_ready

);
  localparam MEM_REQ_SIZE = $size(MEM_REQ);
  //   localparam ISSUE_OP_SIZE = $size(ISSUE_OP);
  logic store_buffer_empty;
  logic store_buffer_i_ready;
  logic store_load_violation_detected, store_load_violation_valid;
  logic o_store_valid, o_store_ready, o_load_valid, o_load_ready;
  MEM_REQ o_store_req, o_load_req, store_load_violation_req;

  // send load req to dmem, store req to store buffer wait for commit
  wire load_i_ready;
  assign i_ready = store_buffer_i_ready & load_i_ready;

  // Send load to dmem and check store load violation
  // When store load violation has happend, resend it continuously untill violation solved
  logic handle_pending_load_req;
  logic pending_load_valid;
  MEM_REQ pending_load_req;
  //   ISSUE_OP pending_load_iop;
  wire load_i_valid = i_valid & store_buffer_i_ready & (i_req.cmd == MEM_READ);
  assign load_i_ready = (~handle_pending_load_req) & o_load_ready;
  // when load op has been sent, save it
  wire pending_load_valid_set = load_i_valid & load_i_ready;
  // when store load violation was solved, clear it
  wire pending_load_valid_clr = (pending_load_valid & (~store_load_violation_detected)&o_load_fire_s1);

  assign handle_pending_load_req = pending_load_valid & (~pending_load_valid_clr);
  assign o_load_valid = (handle_pending_load_req | load_i_valid) & (~rst);
  assign o_load_req = handle_pending_load_req ? pending_load_req : i_req;
  assign store_load_violation_valid = o_load_valid;
  assign store_load_violation_req = o_load_req;
  wire o_load_fire = o_load_valid & o_load_ready;
  logic o_load_fire_s1;
  reg_r #(1) o_load_fire_s1_r (
      .din (o_load_fire),
      .dout(o_load_fire_s1),
      .*
  );
  assign o_dmem_kill_prev = (store_load_violation_detected & o_load_fire_s1) | rst;
  reg_lr #(1) pending_load_valid_r (
      .load(pending_load_valid_set | pending_load_valid_clr),
      .din (pending_load_valid_set),
      .dout(pending_load_valid),
      .*
  );
  reg_l #(MEM_REQ_SIZE  /*+ ISSUE_OP_SIZE*/) pending_load_req_iop_r (
      .load(pending_load_valid_set),
      .din ({i_req  /*, i_iop*/}),
      .dout({pending_load_req  /*, pending_load_iop*/}),
      .*
  );

  wire rsp_is_load = o_valid & o_ready & o_rsp.has_data;
  logic   arbiter_i_valid[1:0];
  logic   arbiter_i_ready[1:0];
  MEM_REQ arbiter_i_data [1:0];
  assign arbiter_i_valid[0] = o_store_valid;
  assign arbiter_i_data[0] = o_store_req;
  assign o_store_ready = arbiter_i_ready[0] & (~rsp_is_load);

  assign arbiter_i_valid[1] = o_load_valid;
  assign arbiter_i_data[1] = o_load_req;
  assign o_load_ready = arbiter_i_ready[1];

  arbiter #(MEM_REQ_SIZE, 2) mem_req_arbiter (
      .i_valid(arbiter_i_valid),
      .i_data (arbiter_i_data),
      .i_ready(arbiter_i_ready),
      .o_valid(o_dmem_valid),
      .o_data (o_dmem_req),
      .o_ready(o_dmem_ready)
  );

  store_buffer #(STQ_SIZE) inst_store_buffer (
      .empty(store_buffer_empty),
      .i_valid(i_valid & load_i_ready & (i_req.cmd == MEM_WRITE)),
      .i_ready(store_buffer_i_ready),
      .o_violation_detected(store_load_violation_detected),
      .i_violation_valid(store_load_violation_valid),
      .i_violation_req(store_load_violation_req),
      .o_dmem_valid(o_store_valid),
      .o_dmem_req(o_store_req),
      .o_dmem_ready(o_store_ready),
      .*
  );

  //Generate load result
  assign o_valid = i_dmem_valid;
  assign o_rsp = i_dmem_rsp;
  assign i_dmem_ready = o_ready;

  assign empty = store_buffer_empty & (~handle_pending_load_req);
  assign o_rob_finish_valid = rsp_is_load | (o_store_ready & o_store_valid);
  assign o_rob_finish_rob_idx = rsp_is_load ? o_rsp.rob_idx : o_store_req.rob_idx;

  logic i_fire_s1;
  reg_r #(1) i_fire_s1_r (
      .din (i_valid & i_ready),
      .dout(i_fire_s1),
      .*
  );
  `PERF_EVENT(store_load_violation_cycle, o_load_fire_s1 & store_load_violation_detected);
  `PERF_EVENT(store_load_violation, i_fire_s1 & store_load_violation_detected);
  `PERF_EVENT(stq_full, ~store_buffer_i_ready);
endmodule


